import json
import os.path
import re
from typing import Dict, Union, Any, Tuple


class SettingsHelper(object):
    PATTERN_KEY = re.compile(r'settings_(\d+)_(.+)')
    PATTERN_PACK_NAMESPACE_KEY = re.compile(r'([^:]+):([\s\S]+)')
    PATTERN_PACK_USER_NAMESPACE_KEY = re.compile(r'(\d+):([^:]+):([\s\S]+)')

    def __init__(self, path: str):
        self.file_path = path
        self.file_dir = os.path.dirname(self.file_path)
        self.settings: Union[None, Dict[str, str]] = None
        self.load()

    def load(self):
        if os.path.exists(self.file_path):
            with open(self.file_path, 'r', encoding='utf8') as file:
                raw = file.read()
                self.settings = json.loads(raw)
                return
        self.settings = dict()

    def save(self):
        if not os.path.exists(self.file_dir):
            os.makedirs(self.file_dir, exist_ok=True)

        with open(self.file_path, 'w', encoding='utf8') as file:
            file.write(json.dumps(self.settings))
            file.flush()

    def put(self, key: str, value: Any):
        self.settings[key] = value

    def get(self, key: str, default: Any = None) -> Any:
        if key in self.settings:
            return self.settings[key]
        else:
            return default

    @staticmethod
    def to_key(user: int, namespace: str) -> str:
        return 'settings_%d_%s' % (user, namespace)

    @staticmethod
    def from_key(key: str) -> Tuple[Union[None, int], Union[None, str]]:
        matched = SettingsHelper.PATTERN_KEY.fullmatch(key)
        if matched:
            return int(matched.group(1)), matched.group(2)
        else:
            return None, None

    @staticmethod
    def pack_namespace_key(namespace: str, key: str) -> str:
        return '%s:%s' % (namespace, key)

    @staticmethod
    def unpack_namespace_key(packed_key: str) -> Tuple[Union[None, str], Union[None, str]]:
        matched = SettingsHelper.PATTERN_PACK_NAMESPACE_KEY.fullmatch(packed_key)
        if matched:
            return matched.group(1), matched.group(2)
        else:
            return None, None

    @staticmethod
    def pack_user_namespace_key(user: int, namespace: str, key: str) -> str:
        return '%d:%s:%s' % (user, namespace, key)

    @staticmethod
    def unpack_user_namespace_key(packed_key: str) -> Tuple[Union[None, int], Union[None, str], Union[None, str]]:
        matched = SettingsHelper.PATTERN_PACK_USER_NAMESPACE_KEY.fullmatch(packed_key)
        if matched:
            return matched.group(1), matched.group(2), matched.group(3)
        else:
            return None, None, None
